/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/**
 *
 * @author jefoutta
 */

import java.io.IOException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Scheduling {

    public volatile static LinkedList<Integer> proc_queue = new LinkedList();
    public volatile static LinkedList<Integer> waitingQueue = new LinkedList();
    public volatile static LinkedList<Integer> finishedQueue = new LinkedList();
    public volatile static boolean IO = true;
    public volatile static boolean quantum = false;
    public volatile static boolean preemptive = false;
    public volatile static boolean semaphore = false;
    public volatile static boolean tick = false;
    public volatile static boolean end = false;
    public volatile static Integer totalTicks = 0;
    public volatile static Integer procAct;
    public static Integer actQueue = null;
    private Integer tempQueue;
    private boolean queuelimit;
    private boolean notempty;
    private ContextSwitch contextSwitch = new ContextSwitch();
    public boolean[] bol = new boolean[4];
    private boolean tryout = true;
    private String temp = "";
    private String temp2 = "";
    private static StringTokenizer tokenizer;
    public static Integer temp1 = 0;

    public static void inputProc(Integer id){
        if(PCB.proc_id.get(id).job == null){
                    PCB.proc_id.get(id).job = getJob(id);
        }
        if(PCB.proc_id.get(id).getState() == 4){
            waitingQueue.addLast(id);
            Log.println("Waiting Queue recibe a : " + PCB.proc_id.get(id).pname);
        }
        else
            if(ProcQueue.procQueues.containsKey(actQueue)){
                ProcQueue.procQueues.get(actQueue).add(id);
                PCB.proc_id.get(id).queue = actQueue;
                Log.println(ProcQueue.procQueues.get(actQueue).name + " Queue recibe a : " + PCB.proc_id.get(id).pname);
            }
    }

    public static void inputProc(Integer idQueue, Integer idProc){
        if(PCB.proc_id.get(idProc).job == null){
            PCB.proc_id.get(idProc).job = getJob(idProc);
        }
        if(PCB.proc_id.get(idProc).getState() == 4){
            waitingQueue.addLast(idProc);
            Log.println("Waiting Queue recibe a : " + PCB.proc_id.get(idProc).pname);
        }
        else
            if(ProcQueue.procQueues.containsKey(idQueue)){
                ProcQueue.procQueues.get(idQueue).add(idProc);
                Log.println(ProcQueue.procQueues.get(idQueue).name + " Queue recibe a : " + PCB.proc_id.get(idProc).pname);
                PCB.proc_id.get(idProc).queue = idQueue;
                if(PCB.proc_id.get(idProc).IO)
                    PCB.proc_id.get(idProc).IO = false;
                if(PCB.proc_id.get(idProc).quantum)
                    PCB.proc_id.get(idProc).quantum = false;
            }
    }

    public static void tickWaiting(){
        boolean temp = true;
        while((temp) && !(waitingQueue.isEmpty())){
            if(PCB.proc_id.get(waitingQueue.peek()).waitingTime ==  5){
                temp1++;
                PCB.proc_id.get(waitingQueue.peek()).setState(2);
                PCB.proc_id.get(waitingQueue.peek()).waitingTime = 0;
                if(ProcQueue.procQueues.get(PCB.proc_id.get(waitingQueue.peek()).queue).preemptive == 1){
                    //inputProc(1, waitingQueue.poll());
                    inputProc(PCB.proc_id.get(waitingQueue.peek()).queue, waitingQueue.poll());
                }
                else
                    if (PCB.proc_id.get(waitingQueue.peek()).quantum){
                        PCB.proc_id.get(waitingQueue.peek()).quantum = false;
                        inputProc(ProcQueue.procQueues.get(PCB.proc_id.get(waitingQueue.peek()).queue).queueOnQuantumEnd, waitingQueue.poll());
                        //inputProc(1, waitingQueue.poll());
                    }
                    else
                        if(PCB.proc_id.get(waitingQueue.peek()).IO){
                            inputProc(ProcQueue.procQueues.get(PCB.proc_id.get(waitingQueue.peek()).queue).queueOnIOEnd, waitingQueue.poll());
                            //inputProc(1, waitingQueue.poll());
                            //System.out.println(waitingQueue.size());
                        }
            }else
                temp = false;
        }
        //System.out.println(waitingQueue.size() + "waiting");
        for(int i = 0; i < waitingQueue.size(); i++){
            PCB.proc_id.get(waitingQueue.get(i)).waiting();
        }
    }

    public static void setJob(Integer pid){
        PCB.proc_id.get(pid).job = PCB.proc_id.get(pid).job - Memory.ip;
    }

    public static Integer getJob(Integer pid){
        
        FileManager tfm = new FileManager();
        Integer nIO = 0;
        Integer nlabel = 0;
        Integer njump = 0;

        StringTokenizer parametros = new StringTokenizer(PCB.proc_id.get(pid).pname," ");
        tfm.open(parametros.nextToken());
        
        ArrayList<String> tempArray = null;
        try {
            tempArray = tfm.readAll();
        } catch (IOException ex) {
            Logger.getLogger(Scheduling.class.getName()).log(Level.SEVERE, null, ex);
        }

        int i = 0;
        while(i < tempArray.size()){
            if(tempArray.get(i).length() == 0){
                tempArray.remove(i);
                i--;
            }else
                if(tempArray.get(i).contains(";")){
                    tempArray.remove(i);
                    i--;
                }
            i++;
        }
        i = 0;
        String s;
        while(i < tempArray.size()){
            if(tempArray.get(i).contains("'")){
                nlabel++;
                tempArray.remove(i);
                i--;
            }
            i++;
        }
        i = 0;
        while(i < tempArray.size()){
            if((tempArray.get(i).toUpperCase().contains("OPENFILE"))||(tempArray.get(i).toUpperCase().contains("READLINE"))||(tempArray.get(i).toUpperCase().contains("ECHO"))||(tempArray.get(i).toUpperCase().contains("CLOSEFILE")))
                nIO++;
            if((tempArray.get(i).toUpperCase()).contains("JMP"))
                njump++;
            i++;
        }
        Integer formula = tempArray.size() - nIO + njump;
        return formula;
    }

    public static void defQueues(ArrayList<String> tempArray){
        /*for(int i = 0; i < tempArray.size();i++){
            tokenizer = new StringTokenizer(tempArray.get(i));
            
        }*/
        System.out.println(tempArray.toString());
        String[] temps;
        actQueue = Integer.parseInt(tempArray.get(0));
        for(int i = 1; i < tempArray.size(); i++){
            temps = tempArray.get(i).split(" ");
            ProcQueue.procQueues.put(Integer.parseInt(temps[0]), new ProcQueue(temps));
        }
    }

    public boolean[] check(Integer algoritm) throws NullPointerException{
        if(tick){
           totalTicks++;
           if(!waitingQueue.isEmpty())
               tickWaiting();
        }

        algoritm = 1;
        
        switch(algoritm){
            case 1:{
                bol[0] = false;
                bol[3] = false;
                if (tick){
                    bol[2] = true;
                    tick = false;
                }

                if(preemptive){
                    if(procAct != null){
                        if(ProcQueue.procQueues.get(PCB.proc_id.get(procAct).queue).isEmpty()){
                            preemptive = false;
                            //PCB.proc_id.get(procAct).setState(2);
                        }
                        else
                            if(ProcQueue.procQueues.get(PCB.proc_id.get(procAct).queue).type == 2){
                                if(PCB.proc_id.get(ProcQueue.procQueues.get(PCB.proc_id.get(procAct).queue).peek()).job >= PCB.proc_id.get(procAct).job){
                                    preemptive = false;
                                    //PCB.proc_id.get(procAct).setState(2);
                                }
                            }else
                                if(ProcQueue.procQueues.get(PCB.proc_id.get(procAct).queue).type == 3)
                                    if(PCB.proc_id.get(ProcQueue.procQueues.get(PCB.proc_id.get(procAct).queue).peek()).priority <= PCB.proc_id.get(procAct).priority){
                                        preemptive = false;
                                        //PCB.proc_id.get(procAct).setState(2);
                                    }
                    }
                }
                
                if ((IO)||(quantum)||(preemptive)||(semaphore)||(end)){
                    if((procAct != null)){
                        if (preemptive){
                            PCB.proc_id.get(procAct).ticks = 0;
                            PCB.proc_id.get(procAct).setState(4);
                        }
                        temp2 = temp2 + procAct;
                        //PCB.proc_id.get(procAct).setState(4);
                        if(PCB.proc_id.get(procAct).getState() == 5){
                            PCB.proc_id.get(procAct).leaveTick(totalTicks);
                            finishedQueue.addLast(procAct);
                            Log.println("Finalizó proceso: " + procAct);
                        }
                        if(!(waitingQueue.contains(procAct))&&(PCB.proc_id.get(procAct).getState() == 4)){
                            PCB.proc_id.get(procAct).leaveTick(totalTicks);
                            if(!semaphore){
                                waitingQueue.addLast(procAct);
                            }
                        }
                            
                        //procAct = null;
                        //ProcQueue.procQueues.get(1).add(procAct);
                        //Scheduling.proc_queue.addLast(procAct);
                    }
                    if(end){
                        procAct = null;
                    }
                    int i = 0;
                    queuelimit = false;
                    notempty = false;
                    while(!queuelimit){
                        tempQueue = ProcQueue.queue_list.get(i);
                        if(!ProcQueue.procQueues.get(tempQueue).isEmpty()){
                            notempty = true;
                            if(PCB.proc_id.get(ProcQueue.procQueues.get(tempQueue).peek()).getState() == 2){
                        //if(PCB.proc_id.get(proc_queue.peek()).getState() == 2){
                                bol[1] = contextSwitch.run(procAct, ProcQueue.procQueues.get(tempQueue).peek());
                                //System.out.println("se hizo contextSwitch con: " + procAct + ProcQueue.procQueues.get(1).peek());
                                PCB.proc_id.get(ProcQueue.procQueues.get(tempQueue).peek()).takeTick(totalTicks);
                                //bol[1] = contextSwitch.run(procAct, proc_queue.peek());
                                IO = false;
                                quantum = false;
                                actQueue = tempQueue;
                                preemptive = false;
                                semaphore = false;
                                end = false;
                                tryout = true;
                                bol[0] = true;
                                return bol;
                            }
                        }else{
                            i++;
                            if(i == ProcQueue.queue_list.size())
                                queuelimit = true;
                            
                        }
                    }
                    if (!notempty){
                        if(preemptive)
                            preemptive = false;
                        if(!waitingQueue.isEmpty()){
                            bol[3] = true;
                        } 
                    }
                }
            }
        }
        return bol;     
    }

    private static Integer TurnaroundTime(){
        Integer temp = 0;
        for(int i = 0; i < PCB.procList.size(); i++)
            temp += PCB.proc_id.get(PCB.procList.get(i)).turnaraundTime;
        temp = temp / PCB.procList.size();
        return temp;
    }

    private static Integer WaitingTime(){
        Integer temp = 0;
        for(int i = 0; i < PCB.procList.size(); i++)
            temp += PCB.proc_id.get(PCB.procList.get(i)).readyWaitingTime;
        temp = temp / PCB.procList.size();
        return temp;
    }

    private static Integer ResponseTime(){
        Integer temp = 0;
        for(int i = 0; i < PCB.procList.size(); i++)
            temp += PCB.proc_id.get(PCB.procList.get(i)).responseTime;
        temp = temp / PCB.procList.size();
        return temp;
    }

    private static Integer ExecutionTime(){
        Integer temp = 0;
        for(int i = 0; i < PCB.procList.size(); i++)
            temp += PCB.proc_id.get(PCB.procList.get(i)).totalTicks;
        temp = temp / PCB.procList.size();
        return temp;
    }

    public static String Stats(){
        String temp = "";
        temp = temp + "-----------Estadisticas de JefoX------------";
        temp = temp + "\n Throughput: " + (PCB.procList.size()/totalTicks);
        temp = temp + "\n Turnaround Time Promedio: " + TurnaroundTime();
        temp = temp + "\n Waiting Time Promedio: " + WaitingTime();
        temp = temp + "\n Response Time Promedio: " + ResponseTime();
        temp = temp + "\n Execution Time Promedio: " + ExecutionTime();
        temp = temp + "\n -----------Estadisticas de Procesos------------\n";

        for(int i = 0; i < PCB.procList.size(); i++){
            temp = temp + PCB.proc_id.get(PCB.procList.get(i)).toString();
        }

        return temp;
    }
}